#include "sys.h"
#include <intrins.h>

// 参数宏
// #define DEBUG
#define N_COUNT_PER_1S              25      // Count计数多少次为1S
#define WKTC_COUNT                  0x7FFE  // 设置这个数值大概掉电后每16秒唤醒一次
#define FORCE_ONLINE_INTERVAL       1800    // 这个数*16秒就强制通电一段时间
#define FORCE_POWER_TIME            300     // 强制上电时间多少秒
#define ENTER_SLEEP_WHILE_NOSHAKE   60     // 多少秒没有振动就断电休眠
#define ENOUGH_SHAKE_TIMES          200     // 1秒内振动了这个次数就就打开电源

typedef enum MyEnum
{
    SLEEP,                         // 睡眠状态
    FIRST_POWERON,                 // 第一次上电
    WAKEUP_BY_SLEEP_COUNT,         // 掉电唤醒器唤醒
    FORCE_POWERON_TIME,            // 强制上电时间
    WAKEUP_BY_SHAKE_IN_TESTTIME,   // 正在测试振动强度
    NORMAL_WORK,                   // 正常工作时间
};

// 运算宏
#define PowerON                     POWER_CTRL = 1;

// IO口声明
sbit POWER_CTRL = P3 ^ 3;
sbit LED = P1 ^ 7;

// 变量定义
int Count = 0;                      // 每50ms就加1,20的整数倍代表1S
int ShakeTimes = 0;                 // 振动次数
int AutoWakeupTimes = 0;            // 用于记录自动唤醒次数
char Status = FIRST_POWERON;        // 默认值为第一次上电

void Uart_Int() interrupt 4
{
    char UART_Code;
    UART_Code = SBUF;
    if(RI)
    {
        RI = 0;
    }
    else
    {
        TI = 0;
        busyTI = 0;
    }
}

void EXTI_Int(void) interrupt 0
{
    if(Status == NORMAL_WORK)
    {
        Count = 0;
    }
    ShakeTimes++;   // 振动次数加1
    EX0 = 0;        // 关闭外部中断
}


void Time0_Int(void) interrupt 1 // 定时器0中断
{
    Count++; // 累计40ms,25次就是1秒钟
}

void PowerOFF(void)
{
    EX0 = 0;                        // 关外部中断
    ET0 = 0;                        // 关定时器中断

    // 发回数据,用于调试
    sprintf("Shake=");
    printf(ShakeTimes, 1);          // 关闭前发送一次振动数据
    sprintf("Count=");
    printf(Count, 1);               // 关闭前发送一次count
    sprintf("AutoWake=");
    printf(AutoWakeupTimes, 1);     // 关闭前发送一次Autowake
    sprintf("\r\n");
    delayms(10);                    // 等待发送完成

    // 清零数据,休眠
    Count = 0;                      // 再次唤醒是为0
    POWER_CTRL = 0;                 // 关电源输出
    LED = 0;                        // 关LED
    ShakeTimes = 0;                 // 振动次数清零
    Status = SLEEP;                 // 状态为睡眠

    // 设置掉电定时唤醒
    WKTCH = 0x80 | (WKTC_COUNT >> 8);
    WKTCL = WKTC_COUNT;

    EX0 = 1;                        // 开外部中断

    // 开始休眠
    PCON |= 0x02;                   // 进入掉电模式
    _nop_();                        // NOP
    _nop_();
    _nop_();
    _nop_();

    LED = 1;                        // 休眠醒来,打开LED
    ET0 = 1;                        // 开定时器中断

    if(ShakeTimes == 0)             // 如果不是振动醒来的, ShakeTimes就会等于0, 用来记录长时间没有振动唤醒,并通电
    {
        AutoWakeupTimes++;
        if(AutoWakeupTimes >= FORCE_ONLINE_INTERVAL) // 达到一定次数,强制上电
        {
            Status = FORCE_POWER_TIME;  // 强制上电时间
            sprintf("ForcePowerONTime\n");
            PowerON
        }
        else // 否则立即关闭
        {
            Status = WAKEUP_BY_SLEEP_COUNT; // 被掉电唤醒器唤醒
        }
    }
    else        // 如果振动次数不为0,则是振动唤醒,但还在测试阶段
    {
        Status = WAKEUP_BY_SHAKE_IN_TESTTIME;
    }
}


void main()
{
    //---------------------------- 串口中断115200
    SCON = 0x50;        //8位数据,可变波特率
    AUXR |= 0x01;       //串口1选择定时器2为波特率发生器
    AUXR |= 0x04;       //定时器2时钟为Fosc,即1T
    T2L = 0xD8;         //设定定时初值
    T2H = 0xFF;         //设定定时初值
    AUXR |= 0x10;       //启动定时器2
    busyTI = 0;         // 表示串口可以发送
    ES = 1;             // 开启中断
    TI = RI = 0;        // 清中断标志
    //---------------------------- 外部中断初始化
    IT0 = 1; // 下降沿中断
    EX0 = 1; // 开中断
    //---------------------------- IO口初始化
    Set0(P1M1, 7);      // P1.7强推挽
    Set1(P1M0, 7);
    Set0(P3M1, 3);      // P3.3强推挽
    Set1(P3M0, 3);
    LED = 1;            // 打开LED
    POWER_CTRL = 0;     // 关电源输出
    //---------------------------- 计时器初始化,40ms中断一次25次为1s
    AUXR &= 0x7F;       //定时器时钟12T模式
    TMOD &= 0xF0;       //设置定时器模式
    TL0 = 0x00;         //设置定时初值
    TH0 = 0x10;         //设置定时初值
    TF0 = 0;            //清除TF0标志
    TR0 = 1;            //定时器0开始计时
    ET0 = 1;            // 开定时器中断
    //---------------------------- 开所有中断
    EA = 1;
    while(1)
    {
        // 打开外部中断
        EX0 = 1;

        switch (Status)
        {
        // 掉电唤醒,不需要通电,只为记录唤醒次数
        case WAKEUP_BY_SLEEP_COUNT:
            sprintf("AfterWakeupBySleepCount-PowerOFF\t");
            PowerOFF();
            break;

        // 强制上电时间,到时间就关闭电源
        case FORCE_POWER_TIME:
            if(Count >= N_COUNT_PER_1S * FORCE_POWER_TIME)
            {
                sprintf("AfterForcePowerON-PowerOFF\t");
                AutoWakeupTimes = 0;
                PowerOFF();
            }
            break;

        // 测试振动强度时间
        case WAKEUP_BY_SHAKE_IN_TESTTIME:
            if(Count >= N_COUNT_PER_1S)
            {
                // 如果振动次数够
                if(ShakeTimes >= ENOUGH_SHAKE_TIMES)
                {
                    Status = NORMAL_WORK;
                    sprintf("ShakeStrong-PowerON\n");
                    PowerON
                }
                else
                {
                    sprintf("WeakShake-PowerOFF\t");
                    PowerOFF();
                }
            }
            break;

        // 正常上电时间
        case NORMAL_WORK:
            if(Count >= N_COUNT_PER_1S * ENTER_SLEEP_WHILE_NOSHAKE)
            {
                sprintf("TooLongWithNoShake-PowerOFF\t");
                PowerOFF();
            }
            break;

        // 第一次上电, 进入睡眠
        case FIRST_POWERON:
            sprintf("FirstPowrON-PowerOFF\t");
            PowerOFF();
            break;
        }

        // 醒着的时候喂狗
        WDT_CONTR = 0x35; // 1.25秒必须喂狗一次
    }
}